package Modele;

import java.util.ArrayList;
import java.util.Iterator;

/**
 * Classe permettant de créer des arbres
 * @author El Meknassi Hamza (10806466) - Migeon Cyril (11007322)
 */
public class Noeud {
    private Noeud pere;
    private ArrayList<Noeud> fils;
    private int x;
    private int y;
    private int profondeur; /* niveau dans l'arbre, va permettre de trouver le plus long chemin  */
    private int profondeurArbre; /* profondeur totale de l'arbre */
    
    /**
     * Constructeur de la classe Noeud
     * @param pere Noeud père
     * @param x abcisse
     * @param y ordonnée
     * @param profondeur profondeur du noeud dans l'arbre 
     */
    public Noeud(Noeud pere, int x, int y, int profondeur){
        this.pere = pere;
        this.fils = new ArrayList<Noeud>();
        this.x = x;
        this.y = y;
        this.profondeur = profondeur;
    }
    
    /**
     * Constructeur de la classe Noeud
     * @param pere père
     * @param x abcisse
     * @param y ordonnée
     * @param profondeur profondeur du noeud dans l'arbre
     * @param profondeurArbre profondeur de l'arbre
     */
    public Noeud(Noeud pere, int x, int y, int profondeur, int profondeurArbre){
        this.pere = pere;
        this.fils = new ArrayList<Noeud>();
        this.x = x;
        this.y = y;
        this.profondeur = profondeur;
        this.profondeurArbre = profondeurArbre;
    }
    
    /**
     * Permet d'ajouter un fils
     * @param x
     * @param y 
     * @return true si la création est possible, false sinon
     */
    public boolean ajouterFils(int x, int y){
        
        /* On évite els boucles infini en regardant si on essaye pas d'ajouter le père du noeud */
        int xPere;
        int yPere;
        
        if(this.getPere() != null)
        {
            xPere = this.getPere().getX();
            yPere = this.getPere().getY();
        }
        else
        {
            xPere = -1;
            yPere = -1;
        }
        
        if(x == xPere && y == yPere)
            return false;
        
        Noeud nouveauFils = new Noeud(this, x, y ,this.profondeur+1, this.profondeurArbre);
        this.fils.add(nouveauFils);
        
        /* On teste si ce noeud etait un des noeuds les plus profond de l'arbre, si oui on maj toutes les profondeurs arbres */
        if(this.profondeur == this.profondeurArbre)
        {
            this.profondeurArbre++;
            Noeud noeud = this;
            while(noeud.pere != null)
                noeud = noeud.pere;
            
            this.setProfondeurArbre(noeud, this.profondeurArbre);
        }
        return true;
    }
    
    /**
     * Retourne la liste de tous les fils du noeud
     * @return fils
     */
    public ArrayList<Noeud> getFils(){
        return this.fils;
    }
    
    /**
     * Retourne le dernier fils Ajouté
     * @return noeud
     */
    public Noeud getDernierFils(){
        int size = this.fils.size();
        
        if(size > 0)
        {
            return this.fils.get(size-1);
        }
        else
        {
            return null;
        }
    }
    
    /**
     * Met à jour tous les noeuds de l'arbre avec la nouvelle profondeurArbre
     * La fonction est toujours appelée depuis ajouterFils, et on donne la racine en noeud
     * @param noeud racine
     * @param profondeurArbre 
     */
    public void setProfondeurArbre(Noeud noeud, int profondeurArbre){
        noeud.profondeurArbre = profondeurArbre;
        Iterator<Noeud> itr = noeud.getFils().iterator();

        /* On utilise la recursivité pour parcourir tous les noeuds */
        while(itr.hasNext())
        {
            noeud = itr.next();
            noeud.setProfondeurArbre(noeud, profondeurArbre);
        }
    }
    
    /**
     * Retourne l'enfant demandé du noeud
     * @param numero numéro de l'enfant qu'on veut obtenir
     * @return fils
     */
    public Noeud getFils(int numero){
        return this.fils.get(numero);
    }
    
    /**
     * Retourne le nombre de fils du noeud
     * @return nbFils
     */
    public int getNbFils(){
        return this.fils.size();
    }
    
    /**
     * Retourne l'abcisse du noeud
     * @return x
     */
    public int getX(){
        return this.x;
    }
    
    /**
     * Retourne l'ordonnée du noeud
     * @return y
     */
    public int getY(){
        return this.y;
    }
    
    /**
     * Retourne le père du noeud
     * @return y
     */
    public Noeud getPere(){
        return this.pere;
    }
    
    /**
     * Retourne la profondeur dans l'arbre du noeud
     * @return profondeur
     */
    public int getProfondeur(){
        return this.profondeur;
    }
    
    /**
     * Retourne la pronfondeur de l'arbre
     * @return pronfondeur de l'arbre
     */
    public int getProfondeurArbre(){
        return this.profondeurArbre-this.profondeur;
    }
    
    /**
     * Retourne un Arraylist avec les plus longs chemins depuis la racine passée en param
     * @param tabPlusLongChemins
     * @param profondeur
     * @param noeud
     */
    public void getPlusLongChemins(ArrayList<int[][]> tabPlusLongChemins, int profondeur, Noeud noeud){        
        Iterator<Noeud> itr = noeud.getFils().iterator();

        /* On utilise la recursivité pour parcourir tous les noeuds */
        while(itr.hasNext())
        {
            noeud = itr.next();
            noeud.getPlusLongChemins(tabPlusLongChemins, profondeur, noeud);
        }
                
        /* Si un noeud est un des plus profond chemin de l'arbre on récupère le chemin complet */
        if(this.getProfondeur() == profondeur)
        {
            tabPlusLongChemins.add(getChemin(noeud, profondeur));
        }            
    }
    
    /**
     * Retourne le chemin vers la racine depuis le noeud passé en paramètre
     * @param noeud
     * @param profondeur 
     * @return chemin
     */
    private int[][] getChemin(Noeud noeud, int profondeur){
        /* On crée un tableau à 2 dimensions avec une dimension pour chaque noeud et une pour avoir l'abcisse et l'ordonnée du noeud */
        int[][] chemin = new int[profondeur+1][2];
        
        for(int i=0; i <= profondeur; i++)
        {
            chemin[profondeur-i][0] = noeud.getX();
            chemin[profondeur-i][1] = noeud.getY();
            noeud = noeud.pere;
        }       
        
        return chemin;
    }
}
